package org.summer.lawyer.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import org.summer.common.core.exception.ServiceException;
import org.summer.common.core.utils.MapstructUtils;
import org.summer.common.json.utils.JsonUtils;
import org.summer.common.mybatis.core.page.PageQuery;
import org.summer.common.mybatis.core.page.TableDataInfo;
import org.summer.common.satoken.utils.LoginHelper;
import org.summer.lawyer.constant.SystemConstants;
import org.summer.lawyer.constant.enums.PayOrderStatus;
import org.summer.lawyer.constant.enums.PaymentMethod;
import org.summer.lawyer.domain.PayOrder;
import org.summer.lawyer.domain.bo.payorder.PayOrderBo;
import org.summer.lawyer.domain.bo.spuorder.SpuOrderBo;
import org.summer.lawyer.domain.bo.spuorderitem.SpuOrderItemBo;
import org.summer.lawyer.domain.vo.payapp.PayAppVo;
import org.summer.lawyer.domain.vo.paychannel.PayChannelVo;
import org.summer.lawyer.domain.vo.payorder.PayOrderVo;
import org.summer.lawyer.domain.vo.spuorder.SpuOrderVo;
import org.summer.lawyer.domain.vo.spuorderitem.SpuOrderItemVo;
import org.summer.lawyer.event.PaymentSuccessEvent;
import org.summer.lawyer.mapper.PayOrderMapper;
import org.summer.lawyer.pay.client.PayClient;
import org.summer.lawyer.service.*;
import org.summer.lawyer.util.IdWorkerUtil;
import org.summer.lawyer.util.ParamsUtil;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 支付订单Service业务层处理
 *
 * @author summer
 * @date 2024-06-22
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class PayOrderServiceImpl extends ServiceImpl<PayOrderMapper, PayOrder> implements IPayOrderService, ApplicationEventPublisherAware {

    private final IPayAppService payAppService;

    private final IPayChannelService payChannelService;

    private final ISpuOrderItemService spuOrderItemService;

    private final ISpuOrderService spuOrderService;

    /**
     * 查询支付订单
     *
     * @param id 主键
     * @return 支付订单
     */
    @Override
    public PayOrderVo selectPayOrderVoById(Long id) {
        return baseMapper.selectVoById(id);
    }

    @Override
    public PayOrderVo selectPayOrderVoByPayNo(String payNo) {
        LambdaQueryWrapper<PayOrder> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(PayOrder::getPayNo, payNo);
        return baseMapper.selectVoOne(queryWrapper, false);
    }

    /**
     * 分页查询支付订单列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 支付订单分页列表
     */
    @Override
    public TableDataInfo<PayOrderVo> selectPageList(PayOrderBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<PayOrder> lqw = buildQueryWrapper(bo);
        Page<PayOrderVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的支付订单列表
     *
     * @param bo 查询条件
     * @return 支付订单列表
     */
    @Override
    public List<PayOrderVo> selectPayOrderVoList(PayOrderBo bo) {
        LambdaQueryWrapper<PayOrder> lqw = Wrappers.lambdaQuery();
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<PayOrder> buildQueryWrapper(PayOrderBo bo) {
        Map<String, Object> params = ParamsUtil.getParams(bo.getParams());
        LambdaQueryWrapper<PayOrder> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getAppId() != null, PayOrder::getAppId, bo.getAppId());
        lqw.eq(bo.getChannelId() != null, PayOrder::getChannelId, bo.getChannelId());
        lqw.eq(StrUtil.isNotBlank(bo.getChannelCode()), PayOrder::getChannelCode, bo.getChannelCode());
        lqw.eq(bo.getUserId() != null, PayOrder::getUserId, bo.getUserId());
        lqw.eq(StrUtil.isNotBlank(bo.getPayNo()), PayOrder::getPayNo, bo.getPayNo());
        lqw.like(StrUtil.isNotBlank(bo.getTradeNo()), PayOrder::getTradeNo, bo.getTradeNo());
        lqw.eq(bo.getOrderId() != null, PayOrder::getOrderId, bo.getOrderId());
        lqw.like(StrUtil.isNotBlank(bo.getSubject()), PayOrder::getSubject, bo.getSubject());
        lqw.like(StrUtil.isNotBlank(bo.getBody()), PayOrder::getBody, bo.getBody());
        lqw.eq(bo.getPayWay() != null, PayOrder::getPayWay, bo.getPayWay());
        lqw.eq(bo.getPayStatus() != null, PayOrder::getPayStatus, bo.getPayStatus());
        lqw.eq(bo.getPayTime() != null, PayOrder::getPayTime, bo.getPayTime());
        lqw.eq(bo.getPayStep() != null, PayOrder::getPayStep, bo.getPayStep());
        lqw.eq(bo.getPostStatus() != null, PayOrder::getPostStatus, bo.getPostStatus());
        lqw.eq(bo.getReturnStatus() != null, PayOrder::getReturnStatus, bo.getReturnStatus());
        lqw.eq(bo.getAmount() != null, PayOrder::getAmount, bo.getAmount());
        lqw.eq(bo.getRefundPrice() != null, PayOrder::getRefundPrice, bo.getRefundPrice());
        lqw.eq(StrUtil.isNotBlank(bo.getResultCode()), PayOrder::getResultCode, bo.getResultCode());
        lqw.eq(StrUtil.isNotBlank(bo.getResultMsg()), PayOrder::getResultMsg, bo.getResultMsg());
        lqw.eq(StrUtil.isNotBlank(bo.getPostJson()), PayOrder::getPostJson, bo.getPostJson());
        lqw.eq(StrUtil.isNotBlank(bo.getReturnJson()), PayOrder::getReturnJson, bo.getReturnJson());
        lqw.eq(bo.getReturnTime() != null, PayOrder::getReturnTime, bo.getReturnTime());
        lqw.eq(StrUtil.isNotBlank(bo.getNotifyUrl()), PayOrder::getNotifyUrl, bo.getNotifyUrl());
        lqw.eq(bo.getProcedureKb() != null, PayOrder::getProcedureKb, bo.getProcedureKb());
        lqw.eq(bo.getProcedureFee() != null, PayOrder::getProcedureFee, bo.getProcedureFee());
        lqw.eq(StrUtil.isNotBlank(bo.getMchNo()), PayOrder::getMchNo, bo.getMchNo());
        lqw.like(StrUtil.isNotBlank(bo.getMchName()), PayOrder::getMchName, bo.getMchName());
        lqw.eq(StrUtil.isNotBlank(bo.getExtParam()), PayOrder::getExtParam, bo.getExtParam());
        lqw.between(params.get("beginTime") != null && params.get("endTime") != null, PayOrder::getPayTime, params.get("beginTime"), params.get("endTime"));
        lqw.orderByDesc(PayOrder::getId);
        return lqw;
    }

    /**
     * 新增支付订单
     *
     * @param bo 支付订单
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(PayOrderBo bo) {
        PayOrder add = MapstructUtils.convert(bo, PayOrder.class);
        validEntityBeforeSave(add);
        add.setCreateTime(DateUtil.date());
        add.setUpdateTime(DateUtil.date());
        if(LoginHelper.getLoginUser() != null){
            add.setCreateBy(LoginHelper.getLoginUser().getUserId());
            add.setUpdateBy(LoginHelper.getLoginUser().getUserId());
        }
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改支付订单
     *
     * @param bo 支付订单
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(PayOrderBo bo) {
        PayOrder update = MapstructUtils.convert(bo, PayOrder.class);
        validEntityBeforeSave(update);
        update.setUpdateTime(DateUtil.date());
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(PayOrder entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除支付订单信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Lock4j(keys = {"#bo.orderId"} ,acquireTimeout = 1000, expire = 10000)
    @Override
    public PayOrderVo sumbitPayOrderVo(PayOrderBo bo) {
        PayChannelVo channelVo = payChannelService.selectPayChannelVoByCode(bo.getChannelCode(), bo.getWxAppId());
        if (ObjectUtil.isNull(channelVo) || SystemConstants.INACTIVE.equals(channelVo.getStatus())) {
            throw new ServiceException("请先配置支付通道");
        }
        PayAppVo payAppVo = payAppService.selectPayAppVoById(channelVo.getAppId());
        if (Objects.isNull(payAppVo)) {
            throw new ServiceException("请先配置支付应用");
        }
        SpuOrderVo orderVo = spuOrderService.selectSpuOrderVoById(bo.getOrderId());
        if (ObjectUtil.isNull(orderVo)) {
            throw new ServiceException(("未找到订单信息"));
        }
        //获取订单子项信息
        SpuOrderItemBo spuOrderItemBo = new SpuOrderItemBo();
        spuOrderItemBo.setOrderId(bo.getOrderId());
        List<SpuOrderItemVo> orderItemVoList = spuOrderItemService.selectSpuOrderItemVoList(spuOrderItemBo);
        if (CollUtil.isNotEmpty(orderItemVoList)) {
            SpuOrderItemVo spuOrderItemVo = orderItemVoList.getFirst();
            bo.setSubject(spuOrderItemVo.getSpuName());
        } else {
            bo.setSubject(StrUtil.format("订单号：{}", orderVo.getOrderNumber()));
        }
        bo.setBody(StrUtil.format("订单号：{}", orderVo.getOrderNumber()));
        bo.setAppId(channelVo.getAppId());
        bo.setWxAppId(channelVo.getWxAppId());
        bo.setMchNo(channelVo.getMchNo());
        bo.setMchName(channelVo.getMchName());
        bo.setChannelId(channelVo.getId());
        bo.setNotifyUrl(StrUtil.format("{}/{}", payAppVo.getPayNotifyUrl(), channelVo.getId()));
        bo.setPayWay(channelVo.getChannelCode());
        //bo.setAmount(orderVo.getPaymentAmount());
        bo.setAmount(1L);
        //支付单编号
        bo.setPayNo(IdWorkerUtil.nextIdStr());
        //订单失效时间
        bo.setExpireTime(DateUtil.offsetMinute(DateUtil.date(), 5).toLocalDateTime());
        bo.setPayStatus(PayOrderStatus.PENDING.getCode());
        bo.setPayTime(DateUtil.date());
        bo.setPayStep(1);
        bo.setPostStatus(1);
        bo.setReturnStatus(0);
        bo.setRefundPrice(0L);
        bo.setPostJson(JsonUtils.toJsonString(bo));
        //提交支付
        PayClient client = payChannelService.getPayClient(channelVo.getId());
        PayOrderVo payResult = client.unifiedPay(bo);
        // 4. 如果调用直接支付成功，则直接更新支付单状态为成功。例如说：付款码支付，免密支付时，就直接验证支付成功
        bo.setPayStatus(payResult.getPayStatus());
        bo.setReturnJson(payResult.getReturnJson());

        bo.setSuccessTime(payResult.getSuccessTime());
        bo.setResultCode(payResult.getResultCode());
        bo.setResultMsg(payResult.getResultMsg());
        if (PayOrderStatus.SUCCESS.getCode().equals(payResult.getPayStatus())) {
            bo.setPayStep(2);
            bo.setReturnStatus(1);
            bo.setReturnTime(DateUtil.date());
            //通知订单完成
            payOrderNotify(payResult);
        }

        this.insertByBo(bo);
        if (PayOrderStatus.CLOSED.getCode().equals(payResult.getPayStatus())) {
            throw new ServiceException(payResult.getResultMsg());
        }
        PayOrderVo vo = selectPayOrderVoById(bo.getId());
        vo.setTimeStamp(payResult.getTimeStamp());
        vo.setNonceStr(payResult.getNonceStr());
        vo.setPackageValue(payResult.getPackageValue());
        vo.setSignType(payResult.getSignType());
        vo.setPaySign(payResult.getPaySign());
        return vo;
    }

    @DSTransactional(rollbackFor = Exception.class)
    @Override
    public void payOrderNotify(PayOrderVo vo) {
        PayOrderVo payOrderVo = selectPayOrderVoByPayNo(vo.getPayNo());
        //处理待支付订单
        if (payOrderVo != null && PayOrderStatus.PENDING.getCode().equals(payOrderVo.getPayStatus())) {
            PayOrder payOrder = new PayOrder();
            payOrder.setId(payOrderVo.getId());
            payOrder.setPayStatus(PayOrderStatus.SUCCESS.getCode());
            payOrder.setPayTime(vo.getPayTime());
            payOrder.setPayStep(2);
            payOrder.setReturnStatus(1);
            payOrder.setReturnTime(DateUtil.date());
            payOrder.setReturnJson(vo.getReturnJson());
            payOrder.setResultCode(vo.getResultCode());
            payOrder.setResultMsg(vo.getResultMsg());
            payOrder.setSuccessTime(vo.getSuccessTime());
            payOrder.setUpdateTime(DateUtil.date());
            payOrder.setTradeNo(vo.getTradeNo());
            payOrder.setUpdateTime(DateUtil.date());
            LambdaUpdateWrapper<PayOrder> updateWrapper = Wrappers.lambdaUpdate();
            updateWrapper.eq(PayOrder::getId,payOrderVo.getId());
            baseMapper.update(payOrder, updateWrapper);
            //处理商品订单
            SpuOrderBo spuOrderBo = new SpuOrderBo();
            spuOrderBo.setOrderId(payOrderVo.getOrderId());
            spuOrderBo.setTransactionId(vo.getTradeNo());
            spuOrderBo.setPaymentAmount(payOrderVo.getAmount());
            spuOrderBo.setPaymentMethod(PaymentMethod.WECHAT_MINIAPP.getCode());
            spuOrderBo.setPaymentTime(payOrderVo.getPayTime());
            spuOrderService.spuOrderNotify(spuOrderBo);
            //发布支付成功通知
            payOrderVo = selectPayOrderVoByPayNo(vo.getPayNo());
            log.info("[payOrderNotify]支付成功回调");
            PaymentSuccessEvent event = new PaymentSuccessEvent(this,payOrderVo);
            applicationEventPublisher.publishEvent(event);
            log.info("[PaymentSuccessEvent]支付成功事件通知发送成功");
        }
    }

    @Override
    public void refundPayOrder(PayOrderBo bo) {
        PayOrderVo payOrderVo = selectPayOrderVoByPayNo(bo.getPayNo());
        if (payOrderVo != null && PayOrderStatus.SUCCESS.getCode().equals(payOrderVo.getPayStatus())) {
            PayOrder payOrder = new PayOrder();
            payOrder.setId(payOrderVo.getId());
            payOrder.setPayStatus(PayOrderStatus.REFUND.getCode());
            payOrder.setRefundPrice(bo.getRefundPrice());
            payOrder.setUpdateTime(DateUtil.date());
            LambdaUpdateWrapper<PayOrder> updateWrapper = Wrappers.lambdaUpdate();
            updateWrapper.eq(PayOrder::getId, payOrderVo.getId());
            baseMapper.update(payOrder, updateWrapper);
        }
    }

    @Override
    public void closePayOrder(PayOrderBo bo) {
        PayOrderVo payOrderVo = selectPayOrderVoByPayNo(bo.getPayNo());
        if (payOrderVo != null && PayOrderStatus.PENDING.getCode().equals(bo.getPayStatus())) {
            PayOrder payOrder = new PayOrder();
            payOrder.setId(payOrderVo.getId());
            payOrder.setPayStatus(PayOrderStatus.CLOSED.getCode());
            LambdaUpdateWrapper<PayOrder> updateWrapper = Wrappers.lambdaUpdate();
            updateWrapper.eq(PayOrder::getId, payOrderVo.getId());
            baseMapper.update(payOrder, updateWrapper);
        }
    }

    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    @Override
    public PayOrderVo createTestPayOrder(PayOrderBo bo) {
        PayChannelVo channelVo = payChannelService.selectPayChannelVoByCode(bo.getChannelCode(), bo.getWxAppId());
        if (ObjectUtil.isNull(channelVo) || SystemConstants.INACTIVE.equals(channelVo.getStatus())) {
            throw new ServiceException("请先配置支付通道");
        }
        PayAppVo payAppVo = payAppService.selectPayAppVoById(channelVo.getAppId());
        if (Objects.isNull(payAppVo)) {
            throw new ServiceException("请先配置支付应用");
        }
        SpuOrderVo orderVo = spuOrderService.selectSpuOrderVoById(bo.getOrderId());
        if (ObjectUtil.isNull(orderVo)) {
            throw new ServiceException(("未找到订单信息"));
        }
        //获取订单子项信息
        SpuOrderItemBo spuOrderItemBo = new SpuOrderItemBo();
        spuOrderItemBo.setOrderId(bo.getOrderId());
        List<SpuOrderItemVo> orderItemVoList = spuOrderItemService.selectSpuOrderItemVoList(spuOrderItemBo);
        if (CollUtil.isNotEmpty(orderItemVoList)) {
            SpuOrderItemVo spuOrderItemVo = orderItemVoList.getFirst();
            bo.setSubject(spuOrderItemVo.getSpuName());
        } else {
            bo.setSubject(StrUtil.format("订单号：{}", orderVo.getOrderNumber()));
        }
        bo.setBody(StrUtil.format("订单号：{}", orderVo.getOrderNumber()));
        bo.setAppId(channelVo.getAppId());
        bo.setWxAppId(channelVo.getWxAppId());
        bo.setMchNo(channelVo.getMchNo());
        bo.setMchName(channelVo.getMchName());
        bo.setChannelId(channelVo.getId());
        bo.setNotifyUrl(StrUtil.format("{}/{}", payAppVo.getPayNotifyUrl(), channelVo.getId()));
        bo.setPayWay(channelVo.getChannelCode());
        //bo.setAmount(orderVo.getPaymentAmount());
        bo.setAmount(1L);
        //支付单编号
        bo.setPayNo(IdWorkerUtil.nextIdStr());
        //订单失效时间
        bo.setExpireTime(DateUtil.offsetMinute(DateUtil.date(), 5).toLocalDateTime());
        bo.setPayStatus(PayOrderStatus.PENDING.getCode());
        bo.setPayTime(DateUtil.date());
        bo.setPayStep(1);
        bo.setPostStatus(1);
        bo.setReturnStatus(0);
        bo.setRefundPrice(0L);
        bo.setPostJson(JsonUtils.toJsonString(bo));
        //提交支付
        // 4. 如果调用直接支付成功，则直接更新支付单状态为成功。例如说：付款码支付，免密支付时，就直接验证支付成功
        bo.setPayStatus(0);
        bo.setReturnJson("");

//        bo.setSuccessTime(payResult.getSuccessTime());
//        bo.setResultCode(payResult.getResultCode());
//        bo.setResultMsg(payResult.getResultMsg());
//        if (PayOrderStatus.SUCCESS.getCode().equals(payResult.getPayStatus())) {
//            bo.setPayStep(2);
//            bo.setReturnStatus(1);
//            bo.setReturnTime(DateUtil.date());
//            //通知订单完成
//            payOrderNotify(payResult);
//        }

        this.insertByBo(bo);
//        if (PayOrderStatus.CLOSED.getCode().equals(payResult.getPayStatus())) {
//            throw new ServiceException(payResult.getResultMsg());
//        }
        PayOrderVo vo = selectPayOrderVoById(bo.getId());
//        vo.setTimeStamp(payResult.getTimeStamp());
//        vo.setNonceStr(payResult.getNonceStr());
//        vo.setPackageValue(payResult.getPackageValue());
//        vo.setSignType(payResult.getSignType());
//        vo.setPaySign(payResult.getPaySign());
        return vo;
    }
}
